package sa.huang;

import hpi_iaas.choreography.schemas.choreography.topology._2006._12.*;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;


public class TopologyHandler {
	private JAXBContext jaxbContext;
	private TTopology topology = null;
	
	/**
	 * algo 3.1
	 * analyze each ml in topology, generate data:
	 * a pair (mc1, mc2) corresponding to each ml.		*  used by ...;
	 * a pair (sender(s), receiver) corresponding to each ml.		*  used by ...;
	 * a name of pa, that is set as value of bindSenderTo of each ml.		*  used by ...;
	 * @param tml
	 * @param data
	 */
	public void analyzeMessageLinksInTopology(Data data)
	{
		TMessageLinks tmls = topology.getMessageLinks();	
		for(TMessageLink tml : tmls.getMessageLink()){
			String ml;																	// <name> value of messageLink
			String receiver, sender1; 				  									
			ArrayList<String> senders = new ArrayList<String>();   						// senders' ParticipantType are a little complex to retrieve, see the comments for analyzePaAndPaTypeInTopology() below
			String receiveActivity, sendActivity;         
			String namespaceURI, prefix;                 
			QName mc1, mc2;															 
			
			ml = tml.getName(); 
			data.setMessageLinksList(ml);													
			
			receiver = tml.getReceiver();

			if(tml.getSender() != null)
			{
				sender1 = tml.getSender();
				data.setParefs_MLMap(ml, sender1, receiver);
			} else
			{
				senders = (ArrayList<String>) tml.getSenders();
				data.setParefs_MLMap(ml, senders, receiver);
				
				sender1 = tml.getSenders().get(0);
			}
			
			receiveActivity = tml.getReceiveActivity();
			sendActivity = tml.getSendActivity().get(0);								// the schema of topology defines that, there could be multiple sendActivity
																						// <xs:attribute name="sendActivity" type="NCNames" /> in topology.xsd.  but
																						// since senders could only be a set of pa of same paType, there could be only one sendActivity  
																						// in each ml, or is it possible that one ml defines multiple message constructs?
			
			namespaceURI = data.getProcess_PaTypeMap().get(data.getPaType_PaMap().get(receiver)).getNamespaceURI();
			prefix = data.getProcess_PaTypeMap().get(data.getPaType_PaMap().get(receiver)).getPrefix();
			mc2 = new QName(namespaceURI, receiveActivity, prefix);
			namespaceURI = data.getProcess_PaTypeMap().get(data.getPaType_PaMap().get(sender1)).getNamespaceURI();
			prefix = data.getProcess_PaTypeMap().get(data.getPaType_PaMap().get(sender1)).getPrefix();
			mc1 = new QName(namespaceURI, sendActivity, prefix);
			
			data.setMessageConstructsList(mc2);
			data.setMessageConstructsList(mc1);		

			data.setConstructs_MLMap(ml, mc1, mc2);

			if(tml.getBindSenderTo()!=null){
				data.setBindSenderTo_MLMap(ml, tml.getBindSenderTo());
			}
			
			
					
		}
		
	}
	
	/**
	 * Analyse participants in topology, initialise data:
	 * paType_PaMap;
	 * 		*  these HashMaps are stored as fields in object data, used by analyzeMessageLink(ml, data);
	 * @param data
	 */
	public void analyzeParticipants(Data data){
		TParticipants tpas = topology.getParticipants();
		
		for(Object paOrPaset : tpas.getParticipantOrParticipantSet()){
			if(paOrPaset instanceof TParticipant){
				data.setParticipantSet(((TParticipant)paOrPaset).getName());
				data.setPaType_PaMap(((TParticipant)paOrPaset).getName(),((TParticipant)paOrPaset).getType());
				if(((TParticipant)paOrPaset).getForEach() != null){
					data.setScopeSet(((TParticipant)paOrPaset).getForEach()); 	// forEach <QName> could be declared in participant, stored in scopeSet
				}
				if(((TParticipant)paOrPaset).getScope() != null){
					data.setScopeSet(((TParticipant)paOrPaset).getScope());		// scope <QName> could be declared in participant, stored in scopeSet
				}																
			}else{
				data.setPaType_PaMap(((TParticipantSet)paOrPaset).getName(),((TParticipantSet)paOrPaset).getType());
				traversePaset(((TParticipantSet)paOrPaset), data);
			}
			
		}
	}
		
	/**
	 * Analyse participantTypes in topology, initialises or updates:
	 * process_PaTypeMap;
	 * nsprefix_ProcessMap;
	 * @param data
	 */
	public void analyzeParticipantTypes(Data data){
		
		TParticipantTypes tpaTypes = topology.getParticipantTypes();
		for(TParticipantType paType : tpaTypes.getParticipantType()){
			data.setProcess_PaTypeMap(paType.getName(), paType.getParticipantBehaviorDescription());
			data.setNsprefix_ProcessMap(paType.getParticipantBehaviorDescription(), paType.getParticipantBehaviorDescription().getPrefix());
		}
	}	

	/**
	 * traverse the participants (and possible nested paSet) in paSet, initialises or updates:
	 * paType_PaMap;
	 * scope_paMap;
	 * scopeSet
	 * 
	 * @param paSet
	 * @param data
	 */
	private void traversePaset(TParticipantSet paSet, Data data) {
		if(paSet.getForEach() != null){
			data.setScopeSet(paSet.getForEach()); //forEach (<List<QName>>) could be declared in participantSet 
		}
		if(paSet.getScope() != null){
			data.setScopeSet(paSet.getScope()); //scope (<QName>) could be declared in participantSet
		}
		for(Object paOrPaset : paSet.getParticipant()){																// traverse pas or paSets 
			
			/*
			 * based on Definition 3.11
			 * scopePa(pa) is set to sc if and only if one of the following three conditions holds:
			 * 1. pa is a single participant reference, and it is associated with sc by a scope attribute in its <participant> declaration.
			 * 2. pa is a single participant reference, and it is associated with sc by a forEach attribute in its <participant> declaration.
			 * 3. Neither condition one nor condition two holds, and we heve sc = ⊥.
			 */
			if(((TParticipant)paOrPaset).getForEach() != null){														
				data.setScope_PaMap(((TParticipant)paOrPaset).getName(),((TParticipant)paOrPaset).getForEach());	// put (pa, forEach) pair into scope_paMap
				data.setScopeSet(((TParticipant)paOrPaset).getForEach());
				//System.out.println(((TParticipant)paOrPaset).getName() + " " + ((TParticipant)paOrPaset).getForEach());
			}

			if(((TParticipant)paOrPaset).getScope() != null){
				data.setScope_PaMap(((TParticipant)paOrPaset).getName(),((TParticipant)paOrPaset).getScope());		// put (pa, scope) pair into scope_paMap
				data.setScopeSet(((TParticipant)paOrPaset).getScope());
			}
			
			data.setPaType_PaMap(((TParticipant)paOrPaset).getName(),paSet.getType());
			data.setParticipantSet(((TParticipant)paOrPaset).getName());
			
		}
		for(Object paOrPaset : paSet.getParticipantSet()){       // if there is no Set in current Set, paSet.getParticipantSet() returns null,  nothing will be done
					System.out.println("        there's paSet : " + ((TParticipantSet)paOrPaset).getParticipantSet().get(0).getName());
			traversePaset(((TParticipantSet)paOrPaset), data);
		}
		
	}
	public void getTNS(Data data){
		data.setTNS(topology.getTargetNamespace());
	}

	
	public void showResult(Data data){
		System.out.println("  ParticipantSet:");
		Iterator<String> it = data.getParticipantSet().iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
		System.out.println("  ML:");
		for(int i=0; i<data.getMessageLinksList().size(); i++){
			System.out.println(data.getMessageLinksList().get(i) + " ");
		} 
		System.out.println();
		
		System.out.println("  MC:");
		for(int i=0; i<data.getMessageConstructsList().size(); i++){
			System.out.println(data.getMessageConstructsList().get(i) + " ");
		}
		System.out.println();
		
		System.out.println("  constructs_ML(ml):");
		for(String key: data.getConstructs_MLMap().keySet()){
			System.out.println(key + ": " + data.getConstructs_MLMap().get(key));
		}
		System.out.println();
		
		System.out.println("  parefs_ML(ml):");
		for(String key: data.getParefs_MLMap().keySet()){
			System.out.println(key + ": " + data.getParefs_MLMap().get(key));
		}
		System.out.println();
		
		System.out.println("  bindSenderTo_ML(ml):");
		for(String key: data.getBindSenderTo_MLMap().keySet()){
			System.out.println(key + ": " + data.getBindSenderTo_MLMap().get(key));
			}
		System.out.println();
		
		System.out.println("  paType_Pa(pa):");
		for(String key: data.getPaType_PaMap().keySet()){
			System.out.println(key + ": " + data.getPaType_PaMap().get(key));			
		}
		
		System.out.println();
		
		System.out.println("  scope_Pa(pa):");
		for(String key: data.getScope_PaMap().keySet()){
			System.out.println(key + ": " + data.getScope_PaMap().get(key));
		}
		System.out.println();
		System.out.println("  scopeSet:");
		for(QName q:data.getScopeSet()){
			System.out.println(q);
		}
		
		
	}

	public TopologyHandler() {
		try {
			jaxbContext = JAXBContext.newInstance("hpi_iaas.choreography.schemas.choreography.topology._2006._12");
		} catch (JAXBException e) {
			System.out.println(e);
		}
		//topology = loadTopology("src/main/resources/BPEL4Chor/taxiApp/taxiapplicationTopology.xml");
		topology = loadTopology("src/main/resources/BPEL4Chor/travelExample/travelExampleTopology.xml");


	}
	public TTopology loadTopology(String filePath) {		
		try {
			Unmarshaller u = jaxbContext.createUnmarshaller();
			topology = (TTopology)((JAXBElement<?>) u.unmarshal( new FileInputStream(filePath))).getValue();			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JAXBException e) {
			e.printStackTrace();
		}
		return topology;
	}
	
	



}
